home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / News / Alexandra.0.82 / Source / XText.subproj / XText.m < prev    next >
Encoding:
Text File  |  1996-01-30  |  11.0 KB  |  538 lines

  1. #import "XText.h"
  2.  
  3. /*    Some of this code is based on other emacs-like Text classes by
  4.     Julie Zelenski, Lee Boynton, and Glen Diener.
  5.  
  6.     There's some ugly hair in here; the Text object is not very well
  7.     designed to support this kind of stuff.  No doubt this will all be
  8.     fixed by NextStep 9.0 ...
  9. */
  10.  
  11. unsigned char GetPrevious(NXStream *s)
  12. {
  13.      int pos, ch;
  14.      
  15.      pos = NXTell(s);
  16.      if (pos <= 0) return EOF;
  17.      NXSeek(s, --pos, NX_FROMSTART);
  18.      ch = NXGetc(s);
  19.      NXUngetc(s);
  20.      return ch;
  21. }
  22.  
  23. @implementation XText(private)
  24.  
  25. - scrollTo:(const NXPoint *)newOrigin
  26. {
  27.     // p. griffin 7/95
  28.     // superview = ClipView
  29.     // [superview superview] = ScrollView
  30.  
  31.     if([superview respondsTo:@selector(rawScroll:)]){
  32.         [[superview constrainScroll:(NXPoint *)newOrigin] rawScroll:newOrigin];
  33.         if ([[superview superview] respondsTo:@selector(reflectScroll:)]){
  34.             [[superview superview] reflectScroll:superview];// romeo romeo
  35.         }
  36.     }
  37.     return self;
  38. }        
  39. @end            
  40.  
  41. @implementation XText
  42.  
  43. - initFrame:(const NXRect *)frameRect text:(const char *)theText
  44.     alignment:(int)mode
  45. {
  46.     // i don't understand why the compiler whines without the (char *) here
  47.     [super initFrame:frameRect text:(const char *)theText alignment:mode];
  48.     posHint = -1;
  49.     xHintPos = -1;
  50.     return self;
  51. }
  52.  
  53. - goto:(int)pos end:(int)end mode:(int)mode
  54. {
  55.     int start;
  56.     
  57.     switch(mode) {
  58.  
  59.     case 0:        // move
  60.         [self setSel:pos :pos];
  61.         [self scrollSelToVisible];
  62.         posHint = -1;
  63.         break;
  64.  
  65.     case 1:        // delete
  66.     case 2:        // cut
  67.         if (pos != end) {
  68.             start = pos;
  69.             if (start > end)
  70.                 { start = end; end = pos; }
  71.             [self disableAutodisplay];
  72.             [self setSel:start :end];
  73.             if (mode == 1)
  74.                 [self delete:self];
  75.             else
  76.                 [self cut:self];
  77.         }
  78.         posHint = -1;
  79.         break;
  80.  
  81.     case 3:        // select
  82.         start = pos;
  83.         if (start > end)
  84.             { start = end; end = pos; }
  85.         // The Text object can't even extend the selection without flashing,
  86.         // unless we disable autodisplay
  87.         if (sp0.cp != spN.cp) [self disableAutodisplay];
  88.         [self setSel:start :end];
  89.         posHint = pos;
  90.         break;
  91.     }
  92.     xHintPos = -1;
  93.     return self;
  94. }
  95.  
  96. - moveChar:(int)cnt mode:(int)mode
  97. {
  98.     int pos, end;
  99.     int max = [self textLength];
  100.     
  101.     if (sp0.cp == posHint) {
  102.         pos = sp0.cp + cnt;
  103.         end = spN.cp;
  104.     } else {
  105.         pos = spN.cp + cnt;
  106.         end = sp0.cp;
  107.     }
  108.     if (pos < 0)
  109.         pos = 0;
  110.     else if (pos > max)
  111.         pos = max;
  112.     return [self goto:pos end:end mode:mode];
  113. }
  114.  
  115. /* NXBGetc - a text stream macro to get the previous character. */
  116.  
  117. typedef struct {
  118.     id text;
  119.     NXTextBlock *block;
  120. } textInfo;
  121.  
  122. static char getPrevious(NXStream *s)
  123. {
  124.     textInfo *info = (textInfo *) s->info;
  125.     NXTextBlock *block = info->block->prior;
  126.  
  127.     if (!block)
  128.         return EOF;
  129.     s->buf_base = block->text;
  130.     s->buf_ptr = s->buf_base + block->chars;
  131.     s->offset -= block->chars;
  132.     info->block = block;
  133.     return *(--s->buf_ptr);
  134. }
  135.  
  136. #define NXBGetc(s) \
  137.     (((s)->buf_base == (s)->buf_ptr) ? getPrevious(s) : \
  138.                                        *(--((s)->buf_ptr)) )
  139.  
  140. - moveWord:(int)cnt mode:(int)mode
  141. {
  142.     NXStream *s = [self stream];
  143.     char c;
  144.     int i, pos, end;
  145.     unsigned char digit_cat = charCategoryTable['0'];
  146.     unsigned char alpha_cat = charCategoryTable['a'];
  147.     unsigned char c_cat;
  148.     BOOL inWord = NO;
  149.  
  150.     if (cnt == 0)
  151.         return self;
  152.     if (sp0.cp == posHint) {
  153.         pos = sp0.cp;
  154.         end = spN.cp;
  155.     } else {
  156.         pos = spN.cp;
  157.         end = sp0.cp;
  158.     }
  159.     NXSeek(s, pos, NX_FROMSTART);
  160.     i = (cnt<0 ? -cnt : cnt);
  161.     while (1) {
  162.         c = (cnt<0 ? NXBGetc(s) : NXGetc(s));
  163.         if (c == EOF) break;
  164.         c_cat = charCategoryTable[c];
  165.         if (c_cat==alpha_cat || c_cat==digit_cat)
  166.             inWord = YES;
  167.         else if (inWord) {
  168.             --i;
  169.             if (i > 0)
  170.                 inWord = NO;
  171.             else
  172.                 break;
  173.         }
  174.     }
  175.     pos = NXTell(s);
  176.     if (c != EOF)
  177.         pos += (cnt<0 ? 1 : -1);
  178.     return [self goto:pos end:end mode:mode];
  179. }
  180.  
  181. /*  line is from an NXSelPt; returns the length of the line.
  182.     too bad this requires grunging in Text's data structures */
  183.  
  184. #define LINE_LENGTH(line) \
  185.     (self->theBreaks->breaks[(line)/sizeof(NXLineDesc)] & 0x3fff)
  186.  
  187. - moveLine:(int)cnt mode:(int)mode
  188. {
  189.     int pos, end, x, dir;
  190.  
  191.     if (sp0.cp == posHint) {
  192.         pos = sp0.cp;
  193.         end = spN.cp;
  194.     } else {
  195.         pos = spN.cp;
  196.         end = sp0.cp;
  197.     }
  198.     if (mode != 0)
  199.         [self disableAutodisplay];
  200.     // collapse and normalize the selection
  201.     [self setSel:pos :pos];
  202.     x = (sp0.cp == xHintPos ? xHint : (sp0.cp - sp0.c1st));
  203.     
  204.     if (cnt < 0) {
  205.         dir = NX_UP;
  206.         cnt = -cnt;
  207.     } else {
  208.         dir = NX_DOWN;
  209.     }
  210.     for (; cnt > 0; --cnt)
  211.         [self moveCaret: dir];
  212.  
  213.     pos = LINE_LENGTH(sp0.line)-1;
  214.     if (x < pos)
  215.         pos = x;
  216.     pos += sp0.c1st;
  217.     [self goto:pos end:end mode:mode];
  218.     xHintPos = pos;
  219.     xHint = x;
  220.     return self;
  221. }
  222.  
  223. - lineBegin:(int)mode
  224. {
  225.     int pos, end;
  226.  
  227.     if (sp0.cp == posHint) {
  228.         pos = sp0.c1st;
  229.         end = spN.cp;
  230.     } else {
  231.         pos = spN.c1st;
  232.         // Text is inconsistent about what line it thinks we're on
  233.         if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
  234.             pos = spN.cp;
  235.         }
  236.         end = sp0.cp;
  237.     }
  238.     return [self goto:pos end:end mode:mode];
  239. }
  240.  
  241. - lineEnd:(int)mode
  242. {
  243.     NXSelPt *sp;
  244.     int pos, end;
  245.  
  246.     if (sp0.cp == posHint) {
  247.         sp = &sp0;
  248.         end = spN.cp;
  249.     } else {
  250.         // need to correct for TBD
  251.         sp = &spN;
  252.         end = sp0.cp;
  253.     }
  254.     pos = sp->c1st + LINE_LENGTH(sp->line) - 1;
  255.     if (pos < sp->cp) {
  256.         // Text is being flakey again; we really want to be on the next line
  257.         // this is pretty gross
  258.         pos = sp->line;
  259.         if (theBreaks->breaks[pos/sizeof(NXLineDesc)] < 0)
  260.             pos += sizeof(NXHeightChange);
  261.         else
  262.             pos += sizeof(NXLineDesc);
  263.         pos = sp->cp + LINE_LENGTH(pos) - 1;
  264.     }
  265.     if ((pos == sp->cp) && (mode != 0))
  266.         ++pos;
  267.     return [self goto:pos end:end mode:mode];
  268. }
  269.  
  270. - docBegin:(int)mode
  271. {
  272.     return [self goto:0
  273.                  end:(sp0.cp == posHint ? spN.cp : sp0.cp)
  274.                  mode:mode];
  275. }
  276.  
  277. - docEnd:(int)mode
  278. {
  279.     return [self goto:[self textLength]
  280.                  end:(sp0.cp == posHint ? spN.cp : sp0.cp)
  281.                  mode:mode];
  282. }
  283.  
  284. - collapseSel:(int)dir
  285. {
  286.     int pos;
  287.  
  288.     if ((dir < 0) || ((dir == 0) && (sp0.cp == posHint)))
  289.         pos = sp0.cp;
  290.     else
  291.         pos = spN.cp;
  292.     return [self goto:pos end:pos mode:0];
  293. }
  294.  
  295. - transChars
  296. {
  297.     int pos = sp0.cp;
  298.     char buf[2], temp;
  299.  
  300.     if (pos == spN.cp) {
  301.         if (pos == (sp0.c1st + LINE_LENGTH(sp0.line) - 1))
  302.             --pos;
  303.         if (pos > 0)
  304.             if ([self getSubstring:buf start:pos-1 length:2] == 2) {
  305.                 temp = buf[1]; buf[1] = buf[0]; buf[0] = temp;
  306.                 [self disableAutodisplay];
  307.                 [self setSel:pos-1 :pos+1];
  308.                 [self replaceSel:buf length:2];
  309.                 return self;
  310.             }
  311.     }
  312.     NXBeep();
  313.     return self;
  314. }
  315.  
  316. - openLine
  317. {
  318.     int pos = sp0.cp;
  319.  
  320.     // don't do anything if there's a non-empty selection
  321.     if (pos == spN.cp) {
  322.         [self replaceSel:"\n"];
  323.         [self setSel:pos :pos];
  324.     } else
  325.         NXBeep();
  326.     return self;
  327. }
  328.  
  329.             
  330. - scroll:(int)pages :(int)lines
  331. {
  332.     NXRect r;
  333.  
  334.     // if our superview isn't a ClipView, we can't scroll
  335.     if ([superview respondsTo:@selector(rawScroll:)]) {
  336.         [superview getBounds:&r];
  337.         r.origin.y += pages*r.size.height + lines*[self lineHeight];
  338.         [self scrollTo:&r.origin];
  339.     } else
  340.         NXBeep();
  341.     return self;
  342. }
  343.  
  344. - scrollIfRO:(int)pages :(int)lines
  345. {
  346.     if (![self isEditable])
  347.         return [self scroll:pages :lines];
  348.     else
  349.         return nil;
  350. }
  351.  
  352. - insertChar:(NXEvent *)event
  353. {
  354.     char c;
  355.  
  356.     c = event->data.key.charCode;
  357.     [self replaceSel:&c length:1];
  358.     return self;
  359. }
  360.  
  361. - insertNextChar
  362. {
  363.     static id action = nil;
  364.  
  365.     if (!action)
  366.         action = [[XTEventMsgAction allocFromZone:[NXApp zone]]
  367.                             initSel:@selector(insertChar:)];
  368.     nextAction = action;
  369.     return self;
  370. }
  371.  
  372. - autoIndent
  373. {
  374.     int pos, end;
  375.     unsigned char buf[2];
  376.  
  377.     // don't do anything if there's a non-empty selection
  378.     if (sp0.cp != spN.cp) {
  379.         NXBeep();
  380.         return self;
  381.     }
  382.     
  383.     if (sp0.cp == posHint) {
  384.         pos = sp0.c1st;
  385.         end = spN.cp;
  386.     } else {
  387.         pos = spN.c1st;
  388.         // Text is inconsistent about what line it thinks we're on
  389.         if (spN.cp == (spN.c1st + LINE_LENGTH(spN.line))){
  390.             pos = spN.cp;
  391.         }
  392.         end = sp0.cp;
  393.     }
  394.     
  395.     [[self hideCaret] disableAutodisplay];  // no need to display yet
  396.     [self replaceSel:"\n" length:1];
  397.  
  398.     while ([self getSubstring:buf start:pos++ length:1] != -1){
  399.         if(buf[0] == ' ' || buf[0] == '\t') [self replaceSel:buf length:1];
  400.         else if(pos == end) break;
  401.         else break;
  402.     }
  403.     
  404.     [[self setAutodisplay:YES] displayIfNeeded];
  405.  
  406.     /* scroll down to the correct line */
  407.     if ([superview respondsTo:@selector(rawScroll:)]) {
  408.         [self scroll:0 :1];
  409.         [self calcLine];
  410.     }
  411.      
  412.     return self;
  413. }
  414.  
  415. - match:(unsigned char *)LR
  416. {
  417.     NXRect oldRect, newRect;
  418.     unsigned char buf[2];
  419.     int count, left_pos, right_pos, utime;        
  420.  
  421.     right_pos = sp0.cp; 
  422.     left_pos = right_pos-1;
  423.     count = 1;
  424.     utime = 300000;
  425.  
  426.     /*    don't do anything if there's a non-empty selection
  427.      *     or not two character */
  428.     
  429.     if (sp0.cp != spN.cp || strlen(LR) != 2) return self;
  430.     
  431.     /* at the beginning of file ? */
  432.     if (left_pos < 0){
  433.         [self replaceSel:&LR[1] length:1];
  434.         return self;        
  435.     }
  436.        
  437.     /* search for the left character */ 
  438.     while([self getSubstring:buf start:left_pos length:1] != -1){
  439.         if(buf[0] == LR[0]) count--;
  440.         else if (buf[0] == LR[1])  count++;
  441.         if(count == 0) break;
  442.         if(left_pos-- == 0) break;
  443.     }
  444.     
  445.     if(count != 0)  {
  446.         [self replaceSel:&LR[1] length:1];
  447.         return self;
  448.     }
  449.  
  450.  
  451.     [self goto:left_pos end:left_pos+1 mode:3];
  452.  
  453.     /* if our superview isn't a ClipView, no scrolling */
  454.     if ([superview respondsTo:@selector(rawScroll:)]) {
  455.         [superview getBounds:&oldRect];
  456.  
  457.         /* scroll to selection */
  458.         [self scrollSelToVisible];
  459.         [superview getBounds:&newRect];
  460.         
  461.         /* add some time for viewing if the text is scrolled */
  462.         if(newRect.origin.y != oldRect.origin.y) utime +=300000;        
  463.     }
  464.  
  465.     [[self window] display];
  466.     
  467.     usleep(utime);
  468.     
  469.     [self goto:right_pos end:right_pos mode:0];    
  470.         
  471.     /* scrollBack */
  472.     if ([superview respondsTo:@selector(rawScroll:)]) {
  473.         [self scrollTo:&oldRect.origin];
  474.     }
  475.         
  476.     [self replaceSel:&LR[1] length:1];
  477.     
  478.     return self;
  479. }
  480.  
  481. - insertKeyCombination:(NXEvent *)event
  482. {
  483.     char code[9];
  484.     int code_len = 0;
  485.     int cc = event->data.key.charCode;
  486.     int f_digit = event->data.key.charCode >> 4;
  487.     int s_digit = event->data.key.charCode & 0xf;
  488.  
  489.     if ((event->flags & NX_ALPHASHIFTMASK) &&
  490.         !(event->flags & NX_SHIFTMASK))     code[code_len++] = 'l';
  491.     if (event->flags & NX_SHIFTMASK)         code[code_len++] = 's';
  492.     if (event->flags & NX_CONTROLMASK)       code[code_len++] = 'c';
  493.     if (event->flags & NX_ALTERNATEMASK)     code[code_len++] = 'a';
  494.     if (event->flags & NX_COMMANDMASK)       code[code_len++] = 'm';
  495.     if (event->flags & NX_NUMERICPADMASK)    code[code_len++] = 'n';
  496.     if (event->flags & NX_HELPMASK)            code[code_len++] = 'h';
  497.  
  498.     if(NXIsPrint(cc) && !NXIsSpace(cc) && !NXIsCntrl(cc)){
  499.         /* should be able to print this character */
  500.         code[code_len++] = 0x27; // '
  501.         code[code_len++] = cc;
  502.     }
  503.     else if(NXIsCntrl(cc) && (event->flags & NX_CONTROLMASK) 
  504.             && !NXIsSpace(cc) && (cc <= 0x1F)){
  505.         /* ordinary control character */
  506.         code[code_len++] = 0x27; // '
  507.         code[code_len++] = (event->flags & NX_SHIFTMASK) ? cc + 0x40: cc+ 0x60;
  508.     }
  509.     else{
  510.         /* cannot print, replace with hex code */
  511.         code[code_len++] = (f_digit < 10) ? '0' + f_digit: 'A'+ f_digit-10;
  512.         code[code_len++] = (s_digit < 10) ? '0'+ s_digit : 'A'+ s_digit-10;
  513.     }
  514.     
  515.     [self replaceSel:code length:code_len];
  516.     return self;
  517. }
  518.  
  519. - insertKeyCombOfNextKey
  520. {
  521.     static id action = nil;
  522.  
  523.     if (!action)
  524.         action = [[XTEventMsgAction alloc]
  525.                         initSel:@selector(insertKeyCombination:)];
  526.     nextAction = action;
  527.     return self;
  528. }
  529.  
  530. - selectText:sender
  531. {
  532.     [self setSel:0:0];
  533.     
  534.     return self;
  535. }
  536.  
  537. @end
  538.